from not_so_free_marker import statics, string_helper
from not_so_free_marker.enums import MarkTypeEnum
from not_so_free_marker.models import RegMatchListHead, RegMatch
import re

# 处理模板的循环
# <list items as item>
#    item.prop
# </list>
# 作用,对于循环内的内容,根据数据的条数重复生成
# 然后对于变量item,变为列表的索引.
# 生成内容为:
# items[0].prop
# items[1].prop
# items[2].prop
from not_so_free_marker.statics import regVar, regIfHead, regElseIf, regListHead
from not_so_free_marker.string_helper import ExpressProc, ExpressProc2


# 处理循环
def process(template_str: str, data: map):
    return __private_process_all_loop(template_str, data)


# 获取模板中循环的标记
def get_loop_tags(template_str: str):
    loop_tags = []
    for h in statics.regListHead.finditer(template_str):
        reg_item = RegMatchListHead()
        reg_item.position = h.start(0)
        reg_item.position_end = h.end(0)
        reg_item.type = MarkTypeEnum.REG_LIST_HEAD
        reg_item.tagStr = h.group(0)
        reg_item.collection = h.group(1)
        if h.group(4):  # 指定了index
            reg_item.item = h.group(3)
            reg_item.index = h.group(4).replace(',', '')
        else:
            reg_item.item = h.group(2)
        loop_tags.append(reg_item)
    for t in statics.regListTail.finditer(template_str):
        reg_item = RegMatch()
        reg_item.position = t.start(0)
        reg_item.position_end = t.end(0)
        reg_item.type = MarkTypeEnum.REG_LIST_TAIL
        reg_item.tagStr = t.group(0)
        loop_tags.append(reg_item)
    # 按照位置排序
    loop_tags.sort(key=lambda a: a.position)
    return loop_tags


def __private_process_all_loop(template_str: str, data: map):
    loop_tags = get_loop_tags(template_str)

    # 没有任何标记
    if len(loop_tags) < 1:
        return template_str

    # 通过stack判断有没有嵌套,如果有,就先处理父循环(此时会生成N个子循环),然后迭代,直到没有嵌套的循环,
    loop_tag_stack = []
    loop_has_nest = False
    processed_str = []
    last_index = 0
    for i in range(len(loop_tags)):
        # 叠加模板中,<list>标签之前的部分
        if loop_tags[i].type == MarkTypeEnum.REG_LIST_HEAD:
            # 把<list>,压栈
            loop_tag_stack.append(loop_tags[i])
        else:
            # 找到</list>,出栈并处理.
            head_item = loop_tag_stack.pop()
            tail_item = loop_tags[i]
            if loop_tag_stack:  # 栈不空,发生了嵌套的情况,打标记准备二次迭代
                loop_has_nest = True
            else:  # 已经是最外层循环,可以开始处理
                # 叠加模板中,<list>标签之前的部分
                processed_str.append(template_str[last_index: head_item.position])
                # 生成list标签之间的内容
                processed_loop_str = __private__replace_loop_text(
                    template_str[head_item.position:tail_item.position_end],
                    head_item, tail_item, data, "")

                processed_str.append(processed_loop_str)
                last_index = tail_item.position_end
    # 添加最后一个</list>后面的模板原文
    processed_str.append(template_str[last_index:])

    if loop_has_nest:  # loop嵌套了,此时内层的loop还没有处理,再次处理.
        return __private_process_all_loop("".join(processed_str), data)
    else:  # loop没有嵌套,直接返回.
        return "".join(processed_str)


# 生成某一个<list></list>标记里面的内容
def __private__replace_loop_text(template_str: str, tag_match_start: RegMatchListHead, tag_match_tail: RegMatch,
                                 data: map, var_path: str):
    # 从模板中获取重复区域的正文
    str_to_repeat = template_str[len(tag_match_start.tagStr):len(template_str) - len(tag_match_tail.tagStr)]

    # 由于<list>通常写在一行,换行符会造成不美观,因此删掉前面的那个换行符,而且只删除第一个(防止模板刻意想多换几行)
    if str_to_repeat[0] == "\n":
        str_to_repeat = str_to_repeat[1:]

    # 替换循环内容中的变量,把item变成items[#i#n#d#e#x#]
    express_replacer = ExpressProc()
    express_replacer.regex = re.compile("\\b{0}\\b".format(tag_match_start.item))
    express_replacer.process_func = lambda m: "{0}.#i#n#d#e#x#".format(tag_match_start.collection)
    express_replacer.formatStr = "${{{0}}}"

    str_to_repeat = regVar.sub(express_replacer.regex_sub_repo, str_to_repeat)
    # if和elseif里的也要替换
    express_replacer.formatStr = "<#if {0}>"
    str_to_repeat = regIfHead.sub(express_replacer.regex_sub_repo, str_to_repeat)
    express_replacer.formatStr = "<#elseif {0}>"
    str_to_repeat = regElseIf.sub(express_replacer.regex_sub_repo, str_to_repeat)

    # 嵌套list里的内容也要替换
    express_replacer = ExpressProc2()
    express_replacer.regex = re.compile("\\b{0}\\b".format(tag_match_start.item))
    express_replacer.process_func = lambda m: "{0}.#i#n#d#e#x#".format(tag_match_start.collection)
    express_replacer.formatStr = "${{{0}}}"
    express_replacer.formatStr = "<#list {0} as {1}>"
    str_to_repeat = regListHead.sub(express_replacer.regex_sub_repo, str_to_repeat)

    # 替换index,把用户指定的${index}变成${索引}
    if tag_match_start.index != "":
        express_replacer = ExpressProc()
        express_replacer.regex = re.compile("\\b{0}\\b".format(tag_match_start.index))
        express_replacer.process_func = lambda m: "#i#n#d#e#x#".format(tag_match_start.collection)
        express_replacer.formatStr = "${{{0}}}"
        str_to_repeat = regVar.sub(express_replacer.regex_sub_repo, str_to_repeat)
        # if和elseif里的也要替换
        express_replacer.formatStr = "<#if {0}>"
        str_to_repeat = regIfHead.sub(express_replacer.regex_sub_repo, str_to_repeat)
        express_replacer.formatStr = "<#elseif {0}>"
        str_to_repeat = regElseIf.sub(express_replacer.regex_sub_repo, str_to_repeat)

        # 嵌套list里的内容也要替换
        express_replacer = ExpressProc2()
        express_replacer.regex = re.compile("\\b{0}\\b".format(tag_match_start.item))
        express_replacer.process_func = lambda m: "{0}.#i#n#d#e#x#".format(tag_match_start.collection)
        express_replacer.formatStr = "${{{0}}}"
        express_replacer.formatStr = "<#list {0} as {1}>"
        str_to_repeat = regListHead.sub(express_replacer.regex_sub_repo, str_to_repeat)

    # 获取元素
    collection_data = string_helper.dotted_str_to_map(tag_match_start.collection, data)
    looped_str = []

    # 根据数组的个数,重复生成内容,
    # 并把#i#n#d#e#x#标记替换成索引号
    for i in range(len(collection_data)):
        looped_str.append(str_to_repeat.replace("#i#n#d#e#x#", str(i)))

    return "".join(looped_str)
